/*
//依赖libevent_cpp库
要求 每个svr都是独立进程，不能一个驱动对象管理多个svr
需要下面写才能工作：

main()
{

	调用本库的api
	EventMgr::Ins().Dispatch();
}
使用例子：
	一般需要先启动所有 acc. 
	调用
	SetAccSeting --只需要一个svr设置就可以了
	init

	未调用 init 而调用其他函数，为定义行为

	OnRevVerifyReq 回调调用完 ADFacadeMgr::NtfVerifyRet 后，就可以调用 CreateSession。
	IADFacade::OnCreateSession 后，就可以和客户端通讯


*/

#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <map>
#include <any>
#include <memory>
#include "libevent_cpp/include/include_all.h"
#include "svr_util/include/singleton.h"
#include "svr_util/include/easy_code.h"
#include "svr_util/include/typedef.h"
#include "svr_util/include/misc.h"
#include "../../acc_proto/include/proto.h"
#include "log_def.h"

namespace proto {
	struct AccAddr
	{
		std::string ip;
		uint16 port=0;

		bool operator<(const AccAddr &other) const; //定义为了支持 set map容器的key
	};

	//用来标识接入client
	struct SessionId
	{
		uint64 cid = 0;		//某个acc连接client的唯一 id
		// acc id。 等于ConMgr::m_vec_con 索引. 0 开始。 
		// 所有svr 配置AdFacadeInitData::vecAccAddr 一样的情况，多个svr意义一样。
		// 如果svr 配置不一样， 注意转递给别的svr就不合适用了。可以用addr识别。
		uint32 accId = -1;
		
		bool operator<(const SessionId &a) const;//定义为了支持 set map容器的key
		bool SendToClient(uint32 cmd, const std::string& msg) const;
		void Clear()
		{
			cid = 0;
			accId = -1;
		}
	};	

	//表示svr 与 client 的会话
	struct Session : public su::EnableWeakPtr<Session>
	{
	private:
		mutable std::any ex; //session额外自定义信息, 比如Player, Account

	public:
		SessionId id;
		std::string remote_ip;
		uint16 remote_port=0;

	public:
		void Clear();
		bool SendToClient(uint32 cmd, const std::string& msg) const;
		//获取绑定的任意类型对象， 一但绑定，没有途径修改类型
		template<class SnEx>
		SnEx *GetEx() const
		{
			if (!ex.has_value())
			{
				ex = SnEx();
			}
		
			return std::any_cast<SnEx>(&ex);
		}

		//设置弱引用, 要求 Player 继承 su::EnableWeakPtr
		template<class Player>
		void SetWeakEx(Player& player) const
		{
			su::WeakPtr<Player>* pw = GetEx<su::WeakPtr<Player>>();
			L_COND_V(pw);
			*pw = player.GetWeakPtr();
		}

		template<class Player>
		Player* GetWeakEx() const
		{
			su::WeakPtr<Player>* pw = GetEx<su::WeakPtr<Player>>();
			L_COND(pw, nullptr);
			return pw->Get();
		}
	};

	// acc driver 事件回调接口
	class IADFacade
	{
		friend class ConToAcc;
		friend class ADFacadeMgr;

		//到注册acc成功回调。 失败不回调。 失败会有出错日志，无法修复，改代码和配置吧
		virtual void OnRegOK(uint32_t accId) {};

		//接收client请求认证的包. 
		//@tmpSnId 临时会话Id，未认证
		virtual void OnRevVerifyReq(const proto::SessionId& tmpSnId, uint32 cmd, const std::string& msg) {};
		//接收client认证后的消息包到
		virtual void OnRevClientMsg(const proto::Session& session, uint32 cmd, const std::string& msg) =0;

		//client断线通知， acc断线也会通知
		virtual void OnClientDisCon(const proto::Session &sn) {};

		//acc 断线通知
		virtual void OnAccDisCon(const std::string& accIp, uint16 accPort) {};
	};

	struct AdFacadeInitData {
		std::vector<AccAddr> vecAccAddr; // acc 地址列表
		uint16 mainCmd = 0; //意义同 服务器组 等删掉，没用了， 和svrId已加能表达了
		uint16 svrId = 0;
		bool isVerifySvr = false;
	};
	
	//外观模式，acc driver 接口
	class ADFacadeMgr : public Singleton<ADFacadeMgr>
	{
		friend class ConToAcc;
		IADFacade* m_iADFacade = nullptr;
	public:
		//acc设置，具体内容参考 MsgAccSeting
		//当未注册时，会请求注册后，自动发送给acc。
		//当已经注册了，自动马上发送给acc。
		void SetAccSeting(const AccSeting& seting);

		bool Init(IADFacade& iADFacade, const AdFacadeInitData &data);

		void GetClientAddr(const SessionId& id, std::function<void(const std::string&, uint16)>cb);

		//通知验证结果. 
		bool NtfVerifyRet(const SessionId &id, bool is_success);


		//广播
		void BroadCastToClient(uint32 cmd, const std::string& msg);

		//广播部分客户端。 
		//@vecSnId 为SessionId列表， 如果从别的svr进程获取的无效(accId是适合本进程的)。 
		void BroadCastToClient(const std::vector<SessionId> &vecSnId, uint32 cmd, const std::string& msg);

		//创建会话， 请求设置 grpId(mainCmd) 转发到当前svrId
		//返回 onRet的const proto::Session* nullptr 表示失败。
		//注意：
		//认证NtfVerifyRet成功，该函数才会成功
		//会话不应该被替换。 同组svr之间， svr应该主动删除sn,然后再让其他svr创建sn
		void CreateSession(const SessionId& id, std::function<void(const proto::Session*)> onRet);

		//通知acc断开session,acc不会断开client
		void DelSession(const SessionId& id);

		//请求acc踢掉client。 成功回调 IADFacade::OnClientDisCon
		bool DisconClient(const SessionId &id);

		//请求acc踢掉all client
		void DisconAllClient();
	
		//设置client请求消息缓存
		//@isCache true 缓存， false 不缓存
		//@cb 回调说明。 true表示成功， false表示会话不需要设置，比如网络断开了等。 用户也不需要针对falsie后续修补处理
		bool SetCache(const SessionId &id, bool isCache, std::function<void(bool)> cb = nullptr);

		const Session *FindSession(const SessionId &id);

		uint32 SessionNum(); //会话总数
	};


}